use hirun::net::AioFd;
use hierr::{Error, Result};

static BODY: [u8; 4 << 20] = [0; 4 << 20];

pub struct HttpReq {
    status: u32,
}

impl HttpReq {
    pub const fn new() -> HttpReq {
        HttpReq { status: 0 }
    }

    fn parse(&mut self, data: &[u8]) {
        for c in data {
            match c {
                b'\r' => {
                    if (self.status & 0x01) == 0 {
                        self.status += 1;
                    } else {
                        self.status = 1;
                    }
                }
                b'\n' => {
                    if (self.status & 0x01) == 1 {
                        self.status += 1;
                    } else {
                        self.status = 0;
                    }
                }
                _ => self.status = 0,
            }

            if self.status == 4 {
                break;
            }
        }
    }

    fn is_ok(&self) -> bool {
        self.status == 4
    }

    fn try_read(&mut self, conn: &mut AioFd<'_>) -> Result<()> {
        let mut buf = [0_u8; 1048];
        match conn.try_recv(&mut buf, 0) {
            Ok(0) => Err(Error::new(libc::EINVAL)),
            Ok(n) => {
                self.parse(&buf[..n]);
                Ok(())
            },
            Err(e) => Err(e),
        }
    }

    pub async fn read(&mut self, conn: &mut AioFd<'_>) -> Result<()> {
        while !self.is_ok() {
            match self.try_read(conn) {
                Ok(_) => {},
                Err(e) if e.errno == libc::EAGAIN => conn.wait_readable().await?,
                Err(e) => return Err(e),
            }
        }
        Ok(())
    }
}

pub struct HttpRsp {
    body_size: usize,
}

impl HttpRsp {
    pub const fn new(body_size: usize) -> Self {
        Self { body_size }
    }

    async fn write_head(&self, conn: &mut AioFd<'_>) -> Result<()> {
        let hdr = format!(
            "HTTP/1.1 200 Ok\r\nContent-Length: {}\r\n\r\n",
            self.body_size
        );
        let _ = conn.send_all(hdr.as_bytes(), 0).await?;
        Ok(())
    }

    pub async fn write(&self, conn: &mut AioFd<'_>) -> Result<()> {
        self.write_head(conn).await?;
        let mut size = self.body_size;
        while size > 0 {
            let len = core::cmp::min(size, BODY.len());
            let _ = conn.send_all(&BODY[..len], 0).await?;
            size -= len;
        }
        Ok(())
    }
}
